#include "dp.hpp"
#include "util.hpp"
#include "conn_ctl.hpp"

#define BOARD_ROW 15 // 棋盘大小
#define BOARD_COL 15
#define USER_COUNT 2  // 用户数量
#define WHITE_CHESS 1 // 白棋颜色
#define BLACK_CHESS 2

/*  规范 room 模块json的规范
{
    "room_id"  int      //房间id
    "optype"   string   //表示动作，比如下棋"put_chess"，或者聊天"chat",
    "self_id"  int      //自身id
    "uid"      int      //哪个用户执行的动作 id
    "white_id" int      //白棋id
    "black_id" int      //黑棋id

    "row"      int      //如果是下棋动作,代表行
    "col"      int      //如果是下棋动作,代表列
    "winner"   int      //如果下棋后,有胜者就填写胜者id,没有就为 0

    "result"   bool     //判断执行结果
    "reason"   string   //结果的描述
    "message"  string   //聊天消息
} */

typedef enum
{
    ROOM_START = 1, // 房间存在
    ROOM_END        // 房间销毁
} room_status;

class room
{
private:
    int _room_id;                         // 房间号
    int _white_id;                        // 白棋的id
    int _black_id;                        // 黑棋的id
    int _chess_count;                     // 房间用户个数
    room_status _status;                  // 房间状态
    user_table *_user_tab;                // 用户表
    conn_ctl *_conn;                      // 管理连接
    std::vector<std::vector<int>> _board; // 棋盘

    // 判断是否胜利
    bool win_game(int begin_row, int begin_col)
    {
        // 四个方向有一个五子连珠就胜利
        if (
            win_game_five(begin_row, begin_col, 1, 0) || // 上下
            win_game_five(begin_row, begin_col, 1, 1) || // 右下和左上
            win_game_five(begin_row, begin_col, 0, 1) || // 左右
            win_game_five(begin_row, begin_col, 1, -1))  // 右上和左下
        {
            return true;
        }
        return false;
    }

    bool win_game_five(int row, int col, int row_offset, int col_offset)
    {
        int chess_color = _board[row][col];
        int sum = 1;

        int begin_row = row + row_offset;
        int begin_col = col + col_offset;
        // 判断一个方向
        while ((begin_row >= 0 && begin_row < BOARD_ROW) &&
               (begin_col >= 0 && begin_col < BOARD_COL) &&
               (chess_color == _board[begin_row][begin_col]))
        {
            sum++;
            begin_row += row_offset;
            begin_col += col_offset;
        }

        row_offset = -row_offset;
        col_offset = -col_offset;

        begin_row = row + row_offset;
        begin_col = col + col_offset;
        while ((begin_row >= 0 && begin_row < BOARD_ROW) &&
               (begin_col >= 0 && begin_col < BOARD_COL) &&
               (chess_color == _board[begin_row][begin_col]))
        {
            sum++;
            begin_row += row_offset;
            begin_col += col_offset;
        }

        return (sum == 5);
    }

public:
    room(user_table *user, conn_ctl *conn, int id) : _user_tab(user), _room_id(id), _conn(conn),
                                                     _chess_count(0), _status(ROOM_START),
                                                     _board(std::vector<std::vector<int>>(BOARD_ROW, std::vector<int>(BOARD_COL, 0))) 
                                                     {
                                                        debug_log("房间 %d创建成功",_room_id);
                                                     };
    ~room() {debug_log("房间 %d销毁",_room_id);};
    void add_white(int id)
    {
        _white_id = id;
        _chess_count++;
    }
    void add_black(int id)
    {
        _black_id = id;
        _chess_count++;
    }
    int get_white() { return _white_id; }
    int get_black() { return _black_id; }
    int get_id() { return _room_id; }
    int get_count() {return _chess_count; };

    // 下棋
    Json::Value put_chess_hdl(const Json::Value &root)
    {
        int user_id = root["uid"].asInt();
        int chess_color = (user_id == _white_id ? WHITE_CHESS : BLACK_CHESS); // 直观点
        int row = root["row"].asInt();
        int col = root["col"].asInt();
        Json::Value ret_value;

        // 判断用户是否在房间内
        if (!_conn->find_room_id(_white_id))
        {
            // 白棋不在房间，黑棋赢
            ret_value["result"] = true;
            ret_value["winner"] = _black_id;
            ret_value["reason"] = "对方掉线,己方胜利";
            return ret_value;
        }
        if (!_conn->find_room_id(_black_id))
        {
            // 黑棋不在房间，白棋赢
            ret_value["result"] = true;
            ret_value["winner"] = _white_id;
            ret_value["reason"] = "对方掉线,己方胜利";
            return ret_value;
        }

        // 没有掉线,判断填写位置是否有棋子
        if (_board[row][col] != 0)
        {
            // 有棋子
            ret_value["result"] = false;
            ret_value["reason"] = "格子有内容，位置错误";
            return ret_value;
        }
        _board[row][col] = chess_color;

        // 判断是否胜利
        if (win_game(row, col))
        {
            ret_value["winner"] = user_id;
            ret_value["reason"] = "连成五子,胜利";
        }
        else
        {
            ret_value["winner"] = 0;
        }
        ret_value["result"] = true;

        return ret_value;
    }

    // 聊天
    Json::Value chat_hdl(const Json::Value &root)
    {
        Json::Value ret_value;
        ret_value["message"] = root["message"].asString();
        ret_value["result"] = true;
        return ret_value;
    }

    // 广播给房间内的其他用户
    void broadcast(const Json::Value &root)
    {
        std::string str;
        bool ret = json_util::serialize(root, str);
        if (!ret)
        {
            debug_log("serialize error");
            return;
        }

        // 获取房间中的所有用户连接，饭后发送消息
        server::connection_ptr ptr = _conn->get_con_room(_white_id); // 白棋id
        if (ptr.get() != nullptr)
        {
            ptr->send(str);
        }
        else
        {
            error_log("获取房间白棋失败");
        }

        ptr = _conn->get_con_room(_black_id);
        if (ptr.get() != nullptr)
        {
            ptr->send(str);
        }
        else
        {
            error_log("获取房间黑棋失败");
        }
    }

    // 调控行为
    void con_handle(const Json::Value &root)
    {
        std::string hdl = root["optype"].asCString(); // 获取行为
        Json::Value ret;
        // 不是同一个房间
        int room_id = root["room_id"].asInt();
        if (room_id != _room_id)
        {
            ret["result"] = false;
            ret["reason"] = "房间号错误";
            ret["optype"] = root["optype"].asString();
            broadcast(ret);
            return;
        }

        if (hdl == "put_chess")
        {
            ret = put_chess_hdl(root);
            int winner_id = ret["winner"].asInt();
            if (winner_id != 0)
            {
                int lose_id = winner_id == _white_id ? _black_id : _white_id;
                _status = ROOM_END;
                if (!_user_tab->win(winner_id))
                {
                    return;
                }
                if (!_user_tab->lose(lose_id))
                {
                    return;
                }
            }
        }
        else if (hdl == "chat")
        {
            // 处理聊天
            ret = chat_hdl(root);
        }
        else // 未知行为
        {
            ret["optype"] = root["optyped"].asCString();
            ret["result"] = false;
            ret["reason"] = "未知命令，执行失败";
        }

        // 拿到处理完的结果,并且打印广播结果看看，方便调试
        std::string str;
        json_util::serialize(ret, str);
        debug_log("执行广播:%s", str.c_str());
        broadcast(ret);
        return;
    }

    // 处理用户退出
    void user_exit(int id)
    {
        // 可能在下棋中退出，这时候直接判断胜利
        Json::Value ret;
        if (_status == ROOM_START)
        {
            ret["room_id"] = _room_id;
            ret["optype"] = "put_chess";
            ret["result"] = false;
            ret["reason"] = "对方退出，胜利";
            int winner_id = _white_id == id ? _black_id : _white_id;
            int lose_id = winner_id == _white_id ? _black_id : _white_id;
            ret["winner"] = winner_id;
            ret["uis"] = id;

            _user_tab->win(winner_id);
            _user_tab->lose(lose_id);
            _status = ROOM_END;
            broadcast(ret);
        }
        _chess_count--;
        return;
    }
};

// 用只能只能管理房间对象
using room_ptr = std::shared_ptr<room>;

// 房间管理模块
class room_ctl
{
private:
    int _next_id;
    std::mutex _mutex;
    user_table *_user_table;
    conn_ctl *_conn;
    std::unordered_map<int, int> _uid_to_rid;       // 用户id对房间id的映射
    std::unordered_map<int, room_ptr> _rid_to_rptr; // 房间id对房间对象的映射;

public:
    room_ctl(user_table *table_tmp, conn_ctl *_conn_tmp) : _user_table(table_tmp), _conn(_conn_tmp)
    {
        _next_id = 1;
    }

    ~room_ctl() {}

    // 增加房间
    room_ptr create_room(int white_id, int black_id)
    {
        // 检查是否两个用户是否在大厅中
        if (!_conn->find_square_id(white_id))
        {
            error_log("%d 用户不在大厅中", white_id);
            return room_ptr();
        }
        
        //有个bug
        // 检查是否两个用户是否在大厅中
        if (!_conn->find_square_id(black_id))
        {
            error_log("%d 用户不在大厅中", black_id);
            return room_ptr();
        }

        // 在大厅中，创建room房间并添加管理记录
        std::unique_lock<std::mutex> lock(_mutex);

        room_ptr room_tmp = std::make_shared<room>(_user_table, _conn, _next_id);
        room_tmp->add_white(white_id);
        room_tmp->add_black(black_id);

        _uid_to_rid.insert(std::make_pair(white_id, _next_id));
        _uid_to_rid.insert(std::make_pair(black_id, _next_id));

        _rid_to_rptr.insert(make_pair(_next_id, room_tmp));
        _next_id++;
        return room_tmp;
    }

    // 通过用户id找到房间对象
    room_ptr find_rptr_by_uid(int uid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _uid_to_rid.find(uid);
        if (it == _uid_to_rid.end())
        {
            // 没找到
            debug_log("没找到房间id");
            return room_ptr();
        }

        int rid = it->second;
        auto ret = _rid_to_rptr.find(rid);
        if (ret == _rid_to_rptr.end())
        {
            // 没找到
            debug_log("没找到房间对象");
            return room_ptr();
        }

        // 找到了
        return ret->second;
    }

    // 通过房间id找到房间对象
    room_ptr find_rptr_by_rid(int rid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _rid_to_rptr.find(rid);
        if (it == _rid_to_rptr.end())
        {
            // 没找到
            debug_log("没找到房间对象");
            return room_ptr();
        }

        return it->second;
    }

    // 通过房间id销毁房间对象
    void delete_room(int rid)
    {
        // 查找房间
        room_ptr ptr = find_rptr_by_rid(rid);
        if (ptr.get() == nullptr)
        {
            debug_log("删除房间错误");
        }

        // 删除房间之前要查看用户是否已经删除了
        int white_id = ptr->get_white();
        int black_id = ptr->get_black();
        std::unique_lock<std::mutex> lock(_mutex);

        _uid_to_rid.erase(white_id);
        _uid_to_rid.erase(black_id);
        _rid_to_rptr.erase(rid);
        return;
    }

    // 删除房间中指定用户，没用户时销毁房间
    void user_exit(int uid)
    {
        // 查找房间
        room_ptr ptr = find_rptr_by_uid(uid);
        if (ptr.get() == nullptr)
        {
            debug_log("删除房间错误");
        }

        ptr->user_exit(uid);
        if(ptr->get_count() == 0)
        {
            delete_room(ptr->get_id());
        }
        return;
    }
};